package com.autumn.workflow.command.task;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.bpmn.model.Process;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.bpmn.model.UserTask;
import org.activiti.engine.impl.interceptor.CommandContext;
import org.springframework.beans.factory.annotation.Autowired;

import com.autumn.platform.core.Utils;
import com.autumn.workflow.command.ParamCommand;
import com.autumn.workflow.model.UserTaskFlow;
import com.autumn.workflow.model.UserTaskNode;

public class InitializeUserTaskNodeAndFlowCommand implements ParamCommand<String, Void> {

    @Autowired
    private UserTaskRepository repository;

    @Override
    public Void execute(CommandContext commandContext, String param) {
        String procDefId = param;
        BpmnModel model = commandContext.getProcessEngineConfiguration().getRepositoryService().getBpmnModel(procDefId);
        for (Process process : model.getProcesses()) {
            this.initialize(process, procDefId);
        }
        return null;
    }

    private void initialize(Process process, String procDefId) {
        // String procDefKey = process.getId();
        String procDefName = process.getName();

        List<UserTaskNode> nodes = new ArrayList<>();
        Map<String, Map<String, String>> flows = new HashMap<>();
        int taskIndex = 1;
        for (FlowElement element : process.getFlowElements()) {
            if (element instanceof UserTask) {
                UserTaskNode node = new UserTaskNode();
                node.setProcDefId(procDefId);
                node.setProcDefName(procDefName);
                node.setTaskKey(element.getId());
                node.setTaskName(element.getName());
                node.setSeqno(taskIndex++);
                nodes.add(node);
            } else if (element instanceof SequenceFlow) {
                SequenceFlow flow = (SequenceFlow) element;
                String srcTaskKey = flow.getSourceRef();
                String destTaskKey = flow.getTargetRef();

                Map<String, String> c = flows.get(srcTaskKey);
                if (null == c) {
                    c = new HashMap<>();
                    flows.put(srcTaskKey, c);
                }
                String expression = flow.getConditionExpression();
                if (Utils.isBlank(expression)) {
                    c.put(destTaskKey, null);
                } else {
                    c.put(destTaskKey, expression.replaceAll("\\s+", ""));
                }
            }
        }

        // 需要注意，由于这里初始化数据时，判断是否回退的逻辑比较粗糙，存在误判的可能，因此有个别数据需要开发人员或业务人员进行校正
        int pathIndex = 1;
        List<UserTaskFlow> paths = new ArrayList<>();
        for (UserTaskNode srcNode : nodes) {
            String srcTaskKey = srcNode.getTaskKey();
            Map<String, String> c = flows.get(srcTaskKey);
            for (UserTaskNode destNode : nodes) {
                String destTaskKey = destNode.getTaskKey();
                String expression = null == c ? null : c.get(destTaskKey);
                boolean include = null != c && c.containsKey(destTaskKey);
                boolean isBack = "${action==0}".equals(expression) || "${action=='0'}".equals(expression) || "${action==\"0\"}".equals(expression);;//表示回退的操作条件

                UserTaskFlow pass = new UserTaskFlow();
                pass.setProcDefId(procDefId);
                pass.setSrcTaskKey(srcTaskKey);
                pass.setDestTaskKey(destTaskKey);
                pass.setAction(1);
                pass.setStatus(!include ? "0" : (isBack ? "0" : "1"));
                pass.setSeqno(pathIndex++);
                paths.add(pass);

                UserTaskFlow back = new UserTaskFlow();
                back.setProcDefId(procDefId);
                back.setSrcTaskKey(srcTaskKey);
                back.setDestTaskKey(destTaskKey);
                back.setAction(0);
                back.setStatus(!include ? "0" : (isBack ? "1" : "0"));
                back.setSeqno(pathIndex++);
                paths.add(back);
            }
        }

        repository.initializeTaskNodeAndFlow(procDefId, nodes, paths);
    }
}
